package logic

import (
	"context"
	"mldong/api/base"
	menuApi "mldong/api/menu"
	dao "mldong/internal/modules/sys/dao"
	"mldong/internal/modules/sys/entity"
	"mldong/internal/modules/sys/enum"
	service "mldong/internal/modules/sys/service"
	"mldong/utility"
	"strings"

	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"github.com/jinzhu/copier"
)

func init() {
	// 注册服务实现
	utility.Register(service.MenuServiceType, NewMenuLogic())
}

type MenuLogic struct {
	menuDao *dao.MenuDao
}

// 保存菜单接口实现
func (logic *MenuLogic) Save(ctx context.Context, req *menuApi.SaveReq) (res bool, err error) {
	if req.AppCode == nil || strings.Trim(*req.AppCode, "") == "" {
		appCode := strings.ToLower(enum.MENU_APP_CODE_PLATFORM.Key) // 先赋值给变量
		req.AppCode = &appCode                                      // 再取地址
	}
	var menu = entity.Menu{}
	err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		req.ID = nil
		// 将req对象赋值给menu
		copier.Copy(&menu, &req)
		// 插入sys_menu表
		menu, err = logic.menuDao.Insert(menu, tx)
		if err != nil {
			return err
		}
		return nil
	})
	return menu.ID != nil, err
}

// 修改菜单接口实现
func (logic *MenuLogic) Update(ctx context.Context, req *menuApi.UpdateReq) (res bool, err error) {
	var menu = entity.Menu{}
	// 将req对象赋值给menu
	copier.Copy(&menu, &req)
	var count int64
	count, err = logic.menuDao.UpdateById(menu, nil)
	if err != nil {
		return false, err
	}
	return count > 0, err
}

// 删除菜单接口实现
func (logic *MenuLogic) Remove(ctx context.Context, ids []string) (res bool, _ error) {
	// 遍历ids
	// for _, id := range ids {
	// 	logic.menuDao.DeleteById(id)
	// }
	var count, err = logic.menuDao.DeleteByIds(ids)
	if err != nil {
		return false, err
	}
	if count == 0 {
		return false, err
	}
	return true, err
}

// 根据ID获取菜单信息
func (logic *MenuLogic) Detail(ctx context.Context, id string) (res menuApi.MenuVO, err error) {
	var menuVO = menuApi.MenuVO{}
	var menu entity.Menu
	menu, err = logic.menuDao.GetById(id)

	// 将menu对象赋值给menuVO
	copier.Copy(&menuVO, &menu)

	return menuVO, err
}

// 分页查询菜单接口实现
func (logic *MenuLogic) Page(ctx context.Context, req *menuApi.PageReq) (res menuApi.MenuPageVO, err error) {
	// 处理分页参数nil值
	utility.HandlePageParam(&req.PageParam)
	menus, total, err := logic.menuDao.Page(*req.PageNum, *req.PageSize, func(model *gdb.Model) *gdb.Model {
		model = utility.BuildQuery(req.PageParam, model)
		if req.OrderBy == "" {
			model = model.OrderAsc("t.sort")
		}
		return model
	})
	var menuVOS = []menuApi.MenuVO{}
	copier.Copy(&menuVOS, &menus)
	menuPage := menuApi.MenuPageVO{
		Rows: menuVOS,
	}
	menuPage.PageNum = *req.PageNum
	menuPage.PageSize = *req.PageSize
	menuPage.RecordCount = total
	// 计算 TotalPage
	if *req.PageSize > 0 {
		menuPage.TotalPage = (total + *req.PageSize - 1) / *req.PageSize
	} else {
		menuPage.TotalPage = 0 // 如果 PageSize 为 0，则 TotalPage 为 0
	}
	return menuPage, err
}

// 查询菜单列表接口实现
func (logic *MenuLogic) List(ctx context.Context, req *menuApi.ListReq) (res []*menuApi.MenuVO, err error) {
	var menuVOS = []*menuApi.MenuVO{}
	menus, err := logic.menuDao.List(func(model *gdb.Model) *gdb.Model {
		model = utility.BuildQuery(req.PageParam, model)
		if req.OrderBy == "" {
			model = model.OrderAsc("t.sort")
		}
		model = model.Where("t.app_code", strings.ToLower(enum.MENU_APP_CODE_PLATFORM.Key))
		return model
	})
	copier.Copy(&menuVOS, &menus)
	if err != nil {
		return nil, err
	}
	return menuVOS, err
}

// 查询菜单树接口实现
func (logic *MenuLogic) Tree(ctx context.Context, req *menuApi.TreeReq) (res []*menuApi.MenuVO, err error) {
	listReq := menuApi.ListReq{}
	copier.Copy(&listReq, &req)
	res, err = logic.List(ctx, &listReq)
	if err != nil {
		return nil, err
	}

	// 使用泛型 TreeTool
	treeTool := &utility.TreeTool[*menuApi.MenuVO]{}
	treeNodes := treeTool.ListToTree(res, "0")

	return treeNodes, nil
}
func (logic *MenuLogic) AppList(ctx context.Context) (res []base.LabelValueVO, err error) {
	dictService := utility.MustResolve(service.DictServiceType).(service.IDictService)
	res, err = dictService.GetByDictType(ctx, "app_list")
	if err != nil {
		return res, err
	}
	if len(res) > 0 {
		return res, nil
	}
	enumMap := utility.GetEnumMap()
	dictModel := enumMap["sys_menu_app_code"]
	for _, item := range dictModel.Items {
		res = append(res, base.LabelValueVO{Label: item.Name, Value: strings.ToLower(item.DictItemKey)})
	}

	return res, nil
}

// menu_logic.go
func (logic *MenuLogic) SyncRoute(ctx context.Context, params []*menuApi.SyncRouteParam) (res bool, err error) {
	// 同步规则:
	// 1.默认只同步四级数据(可用递归，也可使用四层遍历)
	// 2.以appCode,code为唯一标识进行同步
	// 3.只同步isSync=true或isSync=1的数据
	// 4.同步时，有变化时，要更新，也可全字段更新
	// 5.ext扩展信息存放到variable
	// 6.当前端删除路由时，后端对应的也要删除掉

	// 这里需要需要从登录会话中获取
	appCode := strings.ToLower(enum.MENU_APP_CODE_PLATFORM.Key)

	// 开始事务
	err = g.DB().Transaction(context.Background(), func(ctx context.Context, tx gdb.TX) error {
		pidList := []string{"0"}
		menuIds, err := logic.saveMenu(appCode, "0", pidList, params, 1, tx)
		if err != nil {
			return err
		}
		if len(menuIds) == 0 {
			menuIds = append(menuIds, "0")
		}
		// 删除不在前端路由同步的菜单
		now := *gtime.Now()
		_, err = logic.menuDao.Model(tx).Data(g.Map{"is_deleted": 1, "update_time": now}).Where("app_code=?", appCode).Where("is_sync=?", 1).WhereNotIn("id", menuIds).Update()
		if err != nil {
			return err
		}

		return nil
	})

	return err == nil, err
}

// saveMenu 递归保存菜单
func (logic *MenuLogic) saveMenu(appCode, parentId string, pidList []string, params []*menuApi.SyncRouteParam, currentLevel int, tx gdb.TX) (menuIds []string, err error) {
	if len(params) == 0 || currentLevel > 8 {
		return nil, nil
	}

	for _, param := range params {
		if param.IsSync == nil || *param.IsSync != 1 {
			continue
		}

		if param.AppCode == nil {
			appCode := appCode
			param.AppCode = &appCode
		}

		// 查找现有菜单
		menu, err := logic.menuDao.FindOneByAppCodeAndCode(*param.AppCode, *param.Code)
		if err != nil {
			return nil, err
		}

		// 转换扩展信息为JSON
		extJson, err := gjson.New(param.Ext).ToJsonString()
		if err != nil {
			return nil, err
		}

		if menu.ID == nil {
			// 新增菜单
			pids := strings.Join(pidList, ",")
			menu = entity.Menu{}
			copier.Copy(&menu, &param)
			menu.ParentId = &parentId
			menu.Pids = &pids
			menu.Variable = &extJson

			menu, err = logic.menuDao.Insert(menu, tx)
			if err != nil {
				return nil, err
			}
		} else {
			// 更新菜单
			oId := *menu.ID
			oParentId := *menu.ParentId
			copier.Copy(&menu, &param)
			menu.ID = &oId
			menu.ParentId = &oParentId
			menu.Variable = &extJson

			_, err = logic.menuDao.UpdateById(menu, tx)
			if err != nil {
				return nil, err
			}
		}

		// 递归处理子菜单

		newPidList := append(pidList, *menu.ID)
		childMenuIds, err := logic.saveMenu(*param.AppCode, *menu.ID, newPidList, param.Children, currentLevel+1, tx)
		if err != nil {
			return nil, err
		}

		menuIds = append(menuIds, *menu.ID)
		menuIds = append(menuIds, childMenuIds...)
	}

	return menuIds, nil
}
func (logic *MenuLogic) UserRouteMenu(ctx context.Context, userId string, version string) (res []*menuApi.RouteMenuVO, err error) {
	menuList, err := logic.menuDao.List(func(m *gdb.Model) *gdb.Model {
		m = m.Where("enabled", enum.YES_NO_YES.Code)
		m = m.Where("app_code", strings.ToLower(enum.MENU_APP_CODE_PLATFORM.Key))
		m = m.WhereIn("type", []interface{}{enum.MENU_TYPE_DIR.Code, enum.MENU_TYPE_MENU.Code})
		m = m.Where("is_deleted", enum.YES_NO_NO.Code)
		m.OrderAsc("sort")
		return m
	})
	if err != nil {
		return []*menuApi.RouteMenuVO{}, err
	}

	// 初始化权限列表
	var permCodeList []g.Map
	// 根据不同版本处理权限列表
	if strings.ToLower(version) == "art-design-pro" {
		userService := utility.MustResolve(service.UserServiceType).(service.IUserService)
		// art-design-pro版本需要特殊处理权限列表格式
		userPermCodes, err := userService.PermCode(ctx, userId)
		if err != nil {
			return []*menuApi.RouteMenuVO{}, err
		}

		// 将权限码转换为包含authMark和title的字典列表
		for _, permCode := range userPermCodes {
			dict := g.Map{
				"authMark": permCode,
				"title":    permCode,
			}
			permCodeList = append(permCodeList, dict)
		}
	}
	res = []*menuApi.RouteMenuVO{}
	for _, menu := range menuList {
		routeMenu := menuApi.RouteMenuVO{
			ID:        menu.ID,
			ParentId:  menu.ParentId,
			Name:      menu.Code,
			Component: menu.Component,
			Path:      menu.Path,
			Meta: g.Map{
				"order": utility.StringToInt(menu.Sort, 9999999999),
				"title": utility.DerefString(menu.Name),
			},
		}

		// 处理Variable扩展信息
		if menu.Variable != nil && *menu.Variable != "" {
			var variableMap g.Map
			if err := gjson.DecodeTo(*menu.Variable, &variableMap); err == nil {
				for k, v := range variableMap {
					routeMenu.Meta[k] = v
				}
			}
		}

		// 处理图标
		if menu.Icon != nil && *menu.Icon != "" {
			routeMenu.Meta["icon"] = *menu.Icon
		}

		// 处理打开方式
		if menu.OpenType != nil {
			switch *menu.OpenType {
			case enum.MENU_OPEN_TYPE_LINK.Code:
				routeMenu.Meta["link"] = utility.DerefString(menu.Url)
			case enum.MENU_OPEN_TYPE_IFRAME.Code:
				routeMenu.Meta["iframeSrc"] = utility.DerefString(menu.Url)
			}
		}

		// 处理是否显示
		if menu.IsShow != nil && *menu.IsShow == enum.YES_NO_NO.Code {
			if strings.ToLower(version) == "v2" {
				routeMenu.Meta["hideMenu"] = true
			} else {
				routeMenu.Meta["hideInMenu"] = true
			}
		}

		// 处理是否缓存
		if menu.IsCache != nil && *menu.IsCache == enum.YES_NO_YES.Code {
			routeMenu.Meta["keepAlive"] = true
		}

		// 处理重定向
		if redirect, ok := routeMenu.Meta["redirect"].(string); ok && redirect != "" {
			routeMenu.Redirect = &redirect
		}
		// 在处理重定向之后，添加以下逻辑
		if strings.ToLower(version) == "art-design-pro" {
			// 移除link和iframeSrc
			delete(routeMenu.Meta, "link")
			delete(routeMenu.Meta, "iframeSrc")
			// 根据打开类型处理链接
			if menu.OpenType != nil {
				switch *menu.OpenType {
				case enum.MENU_OPEN_TYPE_LINK.Code:
					// 处理内部链接
					routeMenu.Meta["link"] = utility.DerefString(menu.Url)
					routeMenu.Meta["isIframe"] = false
				case enum.MENU_OPEN_TYPE_IFRAME.Code:
					// 处理外部链接
					routeMenu.Meta["link"] = utility.DerefString(menu.Url)
					routeMenu.Meta["isIframe"] = true
				}
			}
			routeMenu.Meta["authList"] = permCodeList
		}
		res = append(res, &routeMenu)
	}
	// 使用泛型 TreeTool
	treeTool := &utility.TreeTool[*menuApi.RouteMenuVO]{}
	treeNodes := treeTool.ListToTree(res, "0")
	return treeNodes, err
}
func NewMenuLogic() service.IMenuService {
	return &MenuLogic{
		menuDao: dao.NewMenuDao(),
	}
}
